﻿#pragma once

#include <functional>
#include <vector>
#include <mutex>
#include <atomic>
#include <memory>

#include "IContext.hpp"

template<typename Field>
class Attribute
{
public:
	typedef std::function<void(bool, const Field &)> Callback;
	typedef void *Subscription;

public:
	explicit Attribute(IStore *store)
		: m_store(store)
	{
	}

	void setValue(const Field &value, bool forceNotify = false)
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		if (m_isValid == false || m_value != value)
		{
			forceNotify = true;
		}
		m_value = value;
		m_isValid = true;
		if (forceNotify)
		{
			notify();
		}
	}

	// if the value is valid return true, other return false.
	bool getValue(Field &value) const
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		value = m_value;
		return m_isValid;
	}

	void invalidate(bool forceNotify = false)
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		if (m_isValid)
		{
			forceNotify = true;
		}
		m_isValid = false;
		if (forceNotify)
		{
			notify();
		}
	}

	bool isValid() const
	{
		return m_isValid;
	}

	operator bool() const
	{
		return isValid();
	}

	Subscription subscribe(Callback callback)
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		m_subscriptions.push_back(std::make_shared<Callback>(callback));
		notify();
		return m_subscriptions.back().get();
	}

	template<typename Target>
	Subscription subscribe(Target *target)
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		Callback callback = [target](bool isValid, const Field &value) {
			target->onStatus(isValid, value);
		};
		m_subscriptions.push_back(std::make_shared<Callback>(callback));
		notify();
		return m_subscriptions.back().get();
	}

	bool unsubscribe(Subscription &subscription)
	{
		std::unique_lock<std::mutex> lock(m_mutex);
		auto itr = m_subscriptions.begin();
		while (itr != m_subscriptions.end())
		{
			if (itr->get() == subscription)
			{
				m_subscriptions.erase(itr);
				return true;
			}
			++itr;
		}
		return false;
	}

protected:
	void notify()
	{
		std::shared_ptr<Field> copy = std::make_shared<Field>(m_value);
		for (auto &callback : m_subscriptions)
		{
			auto fn = [=]() {
				callback->operator()(m_isValid, *copy);
			};
			// push fn to queue;
		}
	}

protected:
	std::atomic_bool m_isValid = false;
	Field m_value{};
	std::vector<std::shared_ptr<Callback>> m_subscriptions;
	mutable std::mutex m_mutex;
	IStore *m_store;
};

template<typename Field>
class Broadcast : public Attribute<Field>
{
public:
	explicit Broadcast(IStore *store)
		: Attribute(store)
	{
	}

private:
	Field getValue() const = delete;
};

template<typename Field>
class Method: public Attribute<Field>
{
public:
	explicit Method(IStore *store)
		: Attribute(store)
	{
	}

private:
	Field getValue() const = delete;
};

